package nl.scalasim
package cli

import annotation.tailrec
import nl.scalasim.controlactors.Choreographer
import scala.actors.Future

/**
 * The command line interface. 
 *
 * @author Yigal Duppen
 * @since Oct 1, 2010
 */
object TileMapPrinter {

  def dumpTerrain(tile: Option[Tile]): Char = {
    tile match {
      case None => 'X'
      case Some(t) if t.structure != None => {
        t.structure.get.descriptor.mnemonic
      }
      case Some(t) => t.terrain match {
        case Water => '~'
        case Mountains => '^'
        case Normal => ' '
        case _ => '?'
      }
    }
  }

  def dump(c: Choreographer, path: Path = collection.immutable.List()): String = {
    val builder = new StringBuilder()
    val m = c.map

    builder ++= "    "
    (0 to m.width - 1) foreach {
      n =>
        builder ++= String.valueOf(n % 10)
    }
    builder ++= "\n----"
    builder ++= "-" * m.width
    builder ++= "\n"

    (0 to m.depth - 1) foreach {
      y =>
        builder ++= String.valueOf(y % 10)
        builder ++= " | "
        (0 to m.width - 1) foreach {
          x =>
            builder += (if (path.contains((x, y))) {
              '#'
            }
            else if (c.hasCitizenAt((x, y))) {
              '@'
            }
            else {
              dumpTerrain(m(x, y))
            })
        }
        builder += '\n'
    }

    builder.toString
  }
}

object CLI extends Application {

  val parser = new CLIParser()
  val setting = new Example1Setting()
  val controller = new Controller(setting)

  @tailrec
  def run() {
    print("> ")
    var command = readLine()
    if (command == "") {
      command = "go"
    }
    parser.parse(command) match {
      case Quit => {
        println("Exiting")
        controller.quit()
        return
      }

      case MalformedCommand(cmd) => {
        println("Malformed version of " + cmd)
      }

      case UnknownCommand => {
        println("Unknown command. Use ? for a list available commands.")
      }

      case Help => {
        println("?\n\tPrint the list of available commands\n" +
                "quit\n\tQuit the game" +

                "\nmap\n\tPrint the current currentActivity of the map, and a legenda\n" +

                "status\n\tPrint the current status of the game. Including at " +
                "least the current amount of money, the current average " +
                "happiness, and the current amount of citizens.\n" +

                "describe <x>,<y>\n\tPrint the description of the terrain, " +
                "citizen and/or structure at the specified location.\n" +

                "go [<n>]\n\tAdvance the simulation by one tick. If the " +
                "optional parameter n has been specified, advance the " +
                "simulation by n ticks.\n" +

                "list\n\tList the available " +
                "structures that can be built, including a small " +
                "description.\n" +

                "build <structure> at <x>,<y>\n\tConstruct a structure with its " +
                "origin at the specified location.\n" +

                "raze <x>,<y>\n\tRemove the structure named name.")
      }

      case List => {
        setting.types filter {_.canBuild} foreach {
          d =>
            println(d.name)
            println("\t" + d.description)
        }
      }

      case Build(descriptor, location) => {
        setting.types find {d => d.name == descriptor} match {
          case None => println("Unknown structure: " + descriptor)
          case Some(d) => {
            if (controller.canAdd(d, location)) {
              controller.add(d, location) match {
                case None => println("Could not add " + descriptor + " at " + location)
                case Some(actor) => println("Added " + descriptor)
              }
            }
            else {
              println("Cannot build " + descriptor + " at " + location)
            }
          }
        }
      }

      case Go(n) => {
        (1 to n) foreach {
          _ =>
            controller.tick()
        }
        println(TileMapPrinter.dump(controller.addressBook.choreographer))
      }

      case ShowMap => {
        println(TileMapPrinter.dump(controller.addressBook.choreographer))
      }

      case Describe(location) => {
        controller.addressBook.choreographer.send(location, GetState) foreach {
          f: Future[Any] =>
            println(f())
        }

      }

      case DebugRoute(from, to) => {
        val start = System.currentTimeMillis
        val path = controller.addressBook.choreographer.map.getPath(from, to) match {
          case None => {
            println("Could not find path")
            collection.immutable.List(from, to)
          }
          case Some(p) => p
        }
        println("Calculation took " + (System.currentTimeMillis - start) + "ms")
        println(TileMapPrinter.dump(controller.addressBook.choreographer, path))
      }

      case n => {
        println(n + " is not yet implemented")
      }
    }

    run()
  }

  println("Welcome to the Scalasim Example Program")
  println(TileMapPrinter.dump(controller.addressBook.choreographer))
  controller.start()
  run()

}